﻿package com.makinolo.rlvforge 
{
import com.makinolo.rlvforge.components.HeightLabel;

import flash.display.*;
import flash.events.MouseEvent;

import mx.controls.TextInput;
import mx.controls.VideoDisplay;
import mx.core.UIComponent;
import mx.core.mx_internal;
import mx.events.FlexEvent;
import mx.events.VideoEvent;

public class VideoProfile extends VideoDisplay
{
//	private const VIEWPORT_DISTANCE = 500;	
	private const BUFFER_POINTS:int = 200;		

	private var currenttime:Number;
	
	private var selectionstart:int;
	private var selectionend:int;
	private var viewstartheight:Number;
	
	private var imagebuffer:Array;
	private var bufferpointer:int;
	private var bufferStartIndex:Array;	
	private var vdata:ProjectData;
	
	public function VideoProfile()
	{
		super();
		this.vdata = null;

		this.imagebuffer = new Array(2);
		this.bufferStartIndex = new Array(2);
		this.bufferStartIndex[0] = 0;
		this.bufferStartIndex[1] = 0;
			
		
		this.addEventListener(VideoEvent.SEEKING, this.refreshData);
		this.addEventListener(VideoEvent.PLAYHEAD_UPDATE, this.refreshData);
		this.addEventListener(FlexEvent.CREATION_COMPLETE, this.creationComplete);
		this.addEventListener(MouseEvent.MOUSE_WHEEL, this.wheelEvent);		
	}

	public function creationComplete(e:FlexEvent):void
	{
		// Mask			
		var largeMask:UIComponent = new UIComponent();
		largeMask.graphics.beginFill(0x00FFFF);
		largeMask.graphics.drawRect(0, 0, this.width, this.height);
		largeMask.graphics.endFill();
		largeMask.x = 0;
		largeMask.y = 0;
		largeMask.cacheAsBitmap = true;
		this.addChild(largeMask);
		this.mask = largeMask;	
				
		// Border
		//this.graphics.lineStyle(2, 0xff9933);
		//this.graphics.drawRect(0,0,this.width,this.height);		

		// Center line
		var cline:UIComponent = new UIComponent();		
		cline.graphics.lineStyle(1, 0x666666);
		cline.graphics.moveTo(this.width/2,0);
		cline.graphics.lineTo(this.width/2,this.height);
		cline.graphics.moveTo(0,this.height/2);
		cline.graphics.lineTo(this.width,this.height/2);
		cline.alpha = 0.5;
		this.addChild(cline);			
	}

	public function clear():void
	{
		this.vdata = null;		
		this.removeChild(imagebuffer[0]);
		this.removeChild(imagebuffer[1]);
		this.imagebuffer[0] = null;
		this.imagebuffer[1] = null;
		mx_internal::videoPlayer.clear();
		this.source = null;
	}

	public function setData(pd:ProjectData):void
	{
		this.vdata = pd;
		this.bufferpointer = 0;
		this.selectionstart = -1;
		this.selectionend = -1;
		this.imagebuffer[0] = null;
		this.imagebuffer[1] = null;			
		this.viewstartheight = this.vdata.getOriginalElevAt(0)-10;
		this.bufferStartIndex[this.bufferpointer] = 0;
		this.bufferStartIndex[this.bufferpointer+1] = bufferStartIndex[this.bufferpointer] + BUFFER_POINTS;			
		currenttime = 0;
		refreshBuffers();		
		this.imagebuffer[0].x = 3000;
		this.imagebuffer[1].x = 3000;
	}

	public function getCurrentFrame(time:Number):Number
	{
		if (this.metadata != null)
			return time*this.metadata.framerate;
		else
			return 0;
	}

	public function refreshData(e:VideoEvent):void 
	{
		this.setTime(this.playheadTime);
	}


	// Va aqui pero hay que llamarla periodicamente mediante un evento del video posiblemente
	public function setTime(time:Number):void
	{					
		if (this.vdata.isReady())
		{
			var d:Number = this.vdata.timeToDistance(time);				
			// si la nueva distancia no se encuentra en los límites que representan los datos
			// renderizados en los bufferes, hay que repintarlos porque nos hemos ido de la vista actual
			var stndx1:Number = bufferStartIndex[this.bufferpointer];
			var endndx1:Number = bufferStartIndex[this.bufferpointer]+BUFFER_POINTS;
			var tp:Number = this.vdata.getTotalPoints();
			if (endndx1 > tp)
				endndx1 = tp-1;
			var stndx2:Number = bufferStartIndex[(this.bufferpointer+1)%2];
			var endndx2:Number = bufferStartIndex[(this.bufferpointer+1)%2]+BUFFER_POINTS;
			if (stndx2 > tp)
				stndx2 = tp;
			if (endndx2 > tp)
				endndx2 = tp-1;	

			if ( (d < this.vdata.getDistanceAt(stndx1) || 
				  d > this.vdata.getDistanceAt(endndx1)) &&
			  	 (d < this.vdata.getDistanceAt(stndx2) || 
				  d > this.vdata.getDistanceAt(endndx2)))			
			{
				bufferpointer = 0;
				var daux:Number = d-(this.width);
				if (daux<0) 
					bufferStartIndex[this.bufferpointer] = 0;
				else
					bufferStartIndex[this.bufferpointer] = Math.floor(this.vdata.distanceToIndex(daux) / BUFFER_POINTS) * BUFFER_POINTS;

				bufferStartIndex[(bufferpointer+1)%2] = bufferStartIndex[bufferpointer] + BUFFER_POINTS;
				// Compruebo si despues del salto el perfil queda fuera de vista debido a las alturas
				// Y lo corrijo
				var sectormaxminh:Array = this.vdata.getMaxMinHeight(bufferStartIndex[bufferpointer], bufferStartIndex[bufferpointer]+BUFFER_POINTS);
				if (this.vdata.getMinHeight() > sectormaxminh[0])
					this.viewstartheight = sectormaxminh[0] - 20;
				else (this.vdata.getMinHeight()-this.height <= 0)
					this.viewstartheight = sectormaxminh[0] - 20;
										
				repaintBuffer(this.bufferpointer);
				repaintBuffer((this.bufferpointer+1)%2);							
					
//trace("salto, poniendo bst="+bufferStartIndex[bufferpointer] );
			}
			
			// Si el final del buffer llega a la parte izquierda de la pantalla, entonces
			// cambiamos el starttime y lo ponemos detras del buffer siguiente y repintamos 
			// el buffer que ya esta fuera de la vista 			
			if (imagebuffer[this.bufferpointer].x + imagebuffer[this.bufferpointer].width < 0)
			{
				bufferStartIndex[this.bufferpointer] += BUFFER_POINTS*2;
				repaintBuffer(this.bufferpointer);
				this.bufferpointer = (this.bufferpointer+1)%2;
//	trace("cambio de buffer "+bufferpointer);
			}
			
			currenttime = time;		
			var xposition:Number = (this.vdata.getDistanceAt(bufferStartIndex[this.bufferpointer]) - d) + (this.width/2) ;			
			this.imagebuffer[this.bufferpointer].x = xposition;
			this.imagebuffer[(this.bufferpointer+1)%2].x = xposition +  this.imagebuffer[this.bufferpointer].width;
		}
	}
	
	// Va aqui seguro: revisar los accesos a pdata y gpsdata
	public function repaintBuffer(bp:int):void
	{		
		var bufst:int = bufferStartIndex[bp];
		var bufend:int = bufferStartIndex[bp]+BUFFER_POINTS;
		var tp:Number = this.vdata.getTotalPoints();
		if ( bufferStartIndex[bp] > tp)
			bufst = tp-1;
		if ( bufferStartIndex[bp]+BUFFER_POINTS > tp)
			bufend = tp-1;
		
trace (bufst+":"+bufend);
		
		if (imagebuffer[bp] != null)
			this.removeChild(imagebuffer[bp]);
		//var buff:Sprite = new Sprite();
		var buff:UIComponent = new UIComponent();
		buff.width = 2000;
		var hdata:TextInput;
		var i:int;
		
		// Selection Fill
		if (this.selectionstart!=-1 && bufst<=selectionstart && bufend>=selectionend)
		{
			buff.graphics.beginFill(0x00aa00);
			buff.graphics.moveTo(this.vdata.getDistanceAt(selectionstart) - this.vdata.getDistanceAt(bufst),
								  this.height - (this.vdata.getElevAt(selectionstart) - this.viewstartheight));
			for (i=selectionstart; i<=selectionend; i++)
			{
				buff.graphics.lineTo(this.vdata.getDistanceAt(i) - this.vdata.getDistanceAt(bufst), 
									 this.height - (this.vdata.getElevAt(i)-this.viewstartheight));
			}
			buff.graphics.lineTo(this.vdata.getDistanceAt(selectionend) - this.vdata.getDistanceAt(bufst), this.height);
			buff.graphics.lineTo(this.vdata.getDistanceAt(selectionstart) - this.vdata.getDistanceAt(bufst), this.height);
			buff.graphics.lineTo(this.vdata.getDistanceAt(selectionstart) - this.vdata.getDistanceAt(bufst), this.height- (this.vdata.getElevAt(selectionstart)-this.viewstartheight));

			buff.graphics.endFill();
		}
		
		// Fixed Profile lines
		buff.graphics.lineStyle(3, 0xaa0000);
		buff.graphics.moveTo(0,this.height - (this.vdata.getElevAt(bufst)-this.viewstartheight));		
		for (i=bufst; i<=bufend ; i++)
		{
			var destpoint:Number; 
			if (this.vdata.dataAvailableAt(i))
			{
				destpoint = this.vdata.getDistanceAt(i) - this.vdata.getDistanceAt(bufst);	
				buff.graphics.lineTo(destpoint, this.height - (this.vdata.getElevAt(i)-this.viewstartheight));
				buff.width = destpoint;
			}		
		}
					
		// Profile lines
		/*buff.graphics.lineStyle(1, 0xffffff);		
		buff.graphics.moveTo(0,this.height - (this.vdata.getOriginalElevAt(bufst)-this.viewstartheight));		
		for (i=bufst; i<=bufend ; i++)
		{
			if (this.vdata.dataAvailableAt(i))
				buff.graphics.lineTo(this.vdata.getDistanceAt(i) - this.vdata.getDistanceAt(bufst), 
									 this.height - (this.vdata.getOriginalElevAt(i)-this.viewstartheight));
				
		}*/

		//buff.graphics.lineTo(pdata[bufend-1].getDistance()- pdata[bufst].getDistance(), 0);
		// Profile points
		buff.graphics.lineStyle(2, 0x000000);
		for (i=bufst; i<bufend ; i++)
		{
			if (this.vdata.dataAvailableAt(i))
			{
				buff.graphics.drawCircle(Math.round(this.vdata.getDistanceAt(i)- this.vdata.getDistanceAt(bufst)),this.height - (this.vdata.getOriginalElevAt(i)-this.viewstartheight), 1);
				// Label con la altura
				//if (gpsdata[i].getElev() != gpsdatafixed[i].getElev())
				if (i%10 == 0)
				{
					hdata = new TextInput();
					hdata.styleName = "infolabel";	
					hdata.width = 45;
					hdata.height= 16;
					hdata.setStyle("fontSize", 8);				
					hdata.text = ""+(this.vdata.getElevAt(i)).toFixed(1)+"m";
					hdata.x = this.vdata.getDistanceAt(i)- this.vdata.getDistanceAt(bufst);
					hdata.y = this.height - (this.vdata.getElevAt(i)-this.viewstartheight) - 15;				
					hdata.visible = true;
					hdata.rotation = -45;								
					buff.addChild(hdata); 					
				} 
				if (this.vdata.getSlopeAt(i) >= 15)
				{
					//var sdata = new SlopeLabel();
					var sdata:HeightLabel = new HeightLabel();
					sdata.text = ""+(this.vdata.getSlopeAt(i)).toFixed(2)+"%";
					sdata.x = this.vdata.getDistanceAt(i)- this.vdata.getDistanceAt(bufst)+5;
					sdata.y = this.height - (this.vdata.getElevAt(i)-this.viewstartheight) - 30;
					sdata.alpha = 0.50;
					buff.addChild(sdata);
				}
				if (this.vdata.getCommentTypeAt(i) == "INFOBOX")
				{
					var iflag:TextInput = new TextInput();
					iflag.styleName = "infobox";
					iflag.text = this.vdata.getCommentAt(i);					
					iflag.height= 20;
					iflag.width = 60;
					iflag.x = this.vdata.getDistanceAt(i)- this.vdata.getDistanceAt(bufst);
					iflag.y = this.height - iflag.height - 1;
					iflag.graphics.lineStyle(1, 0x0f336f);
					iflag.graphics.moveTo(0, 0 );
					iflag.graphics.lineTo(0,-(this.vdata.getElevAt(i)-this.viewstartheight-iflag.height) );					
					buff.addChild(iflag); 
				}
				if (this.vdata.getCommentTypeAt(i) != null &&
					(this.vdata.getCommentTypeAt(i).substr(0,8) == "SEGSTART" ||
					this.vdata.getCommentTypeAt(i).substr(0,6) == "SEGEND" ))
				{
					var sflag:TextInput = new TextInput();
					sflag.styleName = "segment";
					sflag.height= 20;
					sflag.width = 60;
					sflag.text = this.vdata.getCommentTypeAt(i);					
					sflag.x = this.vdata.getDistanceAt(i)- this.vdata.getDistanceAt(bufst);
					//sflag.y =this.height - sflag.height * 2;
					sflag.y = 0;
					sflag.graphics.lineStyle(1, 0xca3a08);
					sflag.graphics.moveTo(0, 0);
					sflag.graphics.lineTo(0, this.height);
					//sflag.graphics.lineTo(0,-(this.vdata.getElevAt(i)-this.viewstartheight-sflag.height*2) );					
					buff.addChild(sflag); 
				}				
			}
		}	
		//buff.visible = false;
		buff.alpha =  0.60;		
		this.addChild(buff);
		this.imagebuffer[bp] = buff;		
	}
		
	public function updateSelection(ini:int, end:int):void
	{		
		if (ini!=-1)
		{
			//selectionstart = this.vdata.timeToIndex(ini);
			//selectionend = this.vdata.timeToIndex(end);
			 
			var d:Number = this.vdata.timeToDistance(ini);
			selectionstart = this.vdata.distanceToIndex(d);
			if (end!=-1)
			{
				d = this.vdata.timeToDistance(end);
				selectionend = this.vdata.distanceToIndex(d);
			}
			else
				selectionend = selectionstart;
		}
		else
			selectionstart = selectionend = -1;

		refreshBuffers();		
	}

	public function wheelEvent(e:MouseEvent):void
	{
		if (this.vdata!=null)
		{
			if (e.delta<0)
				raiseProfile(10);
			else
				raiseProfile(-10);
		}
	}

	public function raiseProfile(v:int):void
	{
		this.viewstartheight += v;
		refreshBuffers();
	}	
		
	public function correctSelectedSlope(slope:Number):void
	{
		this.vdata.correctSlope(this.selectionstart, this.selectionend, slope );
		//this.vdata.fixSlopes();
		refreshBuffers();
	}
	
	public function refreshBuffers():void
	{
		repaintBuffer(bufferpointer);
		repaintBuffer((bufferpointer+1)%2);
		this.setTime(currenttime);
	}

	public function getSelectionStart():int
	{
		return this.selectionstart
	}
	
	public function getSelectionEnd():int
	{
		return this.selectionend
	}
}

}